#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
向日葵Python打包程序
版本: 3.0
功能: 将Python程序打包成exe的GUI工具（增强版）
"""

import sys
import os
import json
import subprocess
import threading
import shutil
import traceback
import time
import re
from pathlib import Path
from datetime import datetime

import tkinter as tk
from tkinter import ttk, messagebox, filedialog

# 图像处理库（用于图标转换）
try:
    from PIL import Image
    PIL_AVAILABLE = True
except ImportError:
    PIL_AVAILABLE = False
    print("提示: PIL/Pillow未安装，图标转换功能受限")
    print("安装命令: pip install Pillow")

# 尝试导入增强UI库
try:
    import customtkinter as ctk
    USE_CTK = True
except ImportError:
    USE_CTK = False
    print("提示: customtkinter未安装，使用基础界面")

# 设置主题
if USE_CTK:
    ctk.set_appearance_mode("light")
    ctk.set_default_color_theme("blue")

# 颜色主题
COLORS = {
    "primary": "#3498db",
    "success": "#2ecc71",
    "warning": "#f39c12",
    "danger": "#e74c3c",
    "secondary": "#95a5a6",
    "dark": "#2c3e50",
    "info": "#9b59b6",
    "sunflower": "#f1c40f",  # 向日葵黄色
}

# 常见的可以安全排除的模块（保留样式相关模块）
DEFAULT_EXCLUDE_MODULES = [
    "test"
]

class PyPackerGUI(ctk.CTk if USE_CTK else tk.Tk):
    """向日葵Python打包程序主窗口"""
    
    def __init__(self):
        super().__init__()
        
        self.packing = False
        self.hidden_imports = []  # 存储隐藏导入
        self.exclude_modules = DEFAULT_EXCLUDE_MODULES.copy()  # 使用默认排除模块
        
        # 设置窗口
        self.title("向日葵Python打包程序 v3.0")
        self.geometry("950x750")
        self.minsize(900, 700)
        
        # 设置窗口图标（如果可能）
        try:
            if sys.platform == "win32":
                self.iconbitmap(default=self._get_default_icon())
        except:
            pass
        
        # 创建UI
        self._create_ui()
        
        # 居中窗口
        self._center_window()
        
        # 检查环境
        self.after(100, self._check_environment)
        
    def _get_default_icon(self):
        """获取默认图标路径"""
        # 这里可以嵌入一个默认的向日葵图标
        return None
        
    def _center_window(self):
        """居中窗口"""
        self.update_idletasks()
        
        screen_width = self.winfo_screenwidth()
        screen_height = self.winfo_screenheight()
        
        window_width = 950
        window_height = 750
        
        x = (screen_width - window_width) // 2
        y = (screen_height - window_height) // 2
        
        self.geometry(f"{window_width}x{window_height}+{x}+{y}")
        
    def _create_ui(self):
        """创建UI界面"""
        # 顶部标题
        self._create_header()
        
        # 主体区域
        if USE_CTK:
            main_container = ctk.CTkFrame(self, fg_color="transparent")
        else:
            main_container = ttk.Frame(self)
        main_container.pack(fill="both", expand=True, padx=10, pady=(0, 10))
        
        # 创建标签页
        if USE_CTK:
            self.tabview = ctk.CTkTabview(main_container)
            self.tabview.pack(fill="both", expand=True)
            
            self.tabview.add("基本设置")
            self.tabview.add("版本信息")
            self.tabview.add("高级选项")
            self.tabview.add("附加资源")
            self.tabview.add("打包输出")
        else:
            self.tabview = ttk.Notebook(main_container)
            self.tabview.pack(fill="both", expand=True)
            
            self.tabs = {}
            for name in ["基本设置", "版本信息", "高级选项", "附加资源", "打包输出"]:
                frame = ttk.Frame(self.tabview)
                self.tabview.add(frame, text=name)
                self.tabs[name] = frame
        
        # 创建各标签页内容
        self._create_basic_tab()
        self._create_version_tab()
        self._create_advanced_tab()
        self._create_resource_tab()
        self._create_output_tab()
        
        # 底部按钮
        self._create_bottom_buttons()
        
    def _create_header(self):
        """创建顶部标题栏"""
        if USE_CTK:
            header_frame = ctk.CTkFrame(
                self,
                height=60,
                fg_color=COLORS["sunflower"],
                corner_radius=10
            )
            header_frame.pack(fill="x", padx=10, pady=(10, 5))
            header_frame.pack_propagate(False)
            
            title_label = ctk.CTkLabel(
                header_frame,
                text="🌻 向日葵Python打包程序",
                font=ctk.CTkFont(size=24, weight="bold"),
                text_color="black"
            )
            title_label.pack(side="left", padx=20, pady=15)
            
            version_label = ctk.CTkLabel(
                header_frame,
                text="v3.0",
                font=ctk.CTkFont(size=12),
                text_color="black"
            )
            version_label.pack(side="left", padx=10, pady=20)
        else:
            header_frame = ttk.Frame(self)
            header_frame.pack(fill="x", padx=10, pady=(10, 5))
            
            title_label = ttk.Label(
                header_frame,
                text="🌻 向日葵Python打包程序 v3.0",
                font=("", 20, "bold")
            )
            title_label.pack(side="left", padx=20, pady=10)
        
    def _get_tab(self, name):
        """获取标签页"""
        if USE_CTK:
            return self.tabview.tab(name)
        else:
            return self.tabs[name]
            
    def _create_basic_tab(self):
        """创建基本设置标签页"""
        tab = self._get_tab("基本设置")
        
        # Python文件选择
        file_frame = self._create_frame(tab, "选择Python文件")
        file_frame.pack(fill="x", padx=10, pady=10)
        
        path_container = self._create_container(file_frame)
        
        self.script_path_var = tk.StringVar()
        if USE_CTK:
            self.script_entry = ctk.CTkEntry(
                path_container,
                textvariable=self.script_path_var,
                placeholder_text="选择要打包的Python文件",
                height=40
            )
        else:
            self.script_entry = ttk.Entry(path_container, textvariable=self.script_path_var)
        self.script_entry.pack(side="left", fill="x", expand=True, padx=(0, 10))
        
        if USE_CTK:
            ctk.CTkButton(
                path_container,
                text="选择文件",
                command=self._select_script,
                width=100,
                height=40,
                fg_color=COLORS["primary"]
            ).pack(side="left")
        else:
            ttk.Button(
                path_container,
                text="选择文件",
                command=self._select_script
            ).pack(side="left")
        
        # 输出设置
        output_frame = self._create_frame(tab, "输出设置")
        output_frame.pack(fill="x", padx=10, pady=10)
        
        out_container = self._create_container(output_frame)
        
        # 输出目录
        dir_frame = self._create_row(out_container)
        if USE_CTK:
            ctk.CTkLabel(dir_frame, text="输出目录:", width=80).pack(side="left")
        else:
            ttk.Label(dir_frame, text="输出目录:", width=10).pack(side="left")
        
        self.output_dir_var = tk.StringVar()
        if USE_CTK:
            ctk.CTkEntry(
                dir_frame,
                textvariable=self.output_dir_var,
                placeholder_text="选择输出目录"
            ).pack(side="left", fill="x", expand=True, padx=(0, 10))
            
            ctk.CTkButton(
                dir_frame,
                text="浏览",
                command=self._select_output_dir,
                width=80
            ).pack(side="left")
        else:
            ttk.Entry(dir_frame, textvariable=self.output_dir_var).pack(side="left", fill="x", expand=True, padx=(0, 10))
            ttk.Button(dir_frame, text="浏览", command=self._select_output_dir).pack(side="left")
        
        # 应用名称
        name_frame = self._create_row(out_container)
        if USE_CTK:
            ctk.CTkLabel(name_frame, text="应用名称:", width=80).pack(side="left")
        else:
            ttk.Label(name_frame, text="应用名称:", width=10).pack(side="left")
        
        self.app_name_var = tk.StringVar()
        if USE_CTK:
            ctk.CTkEntry(
                name_frame,
                textvariable=self.app_name_var,
                placeholder_text="输入应用程序名称"
            ).pack(side="left", fill="x", expand=True)
        else:
            ttk.Entry(name_frame, textvariable=self.app_name_var).pack(side="left", fill="x", expand=True)
        
        # 打包选项
        options_frame = self._create_frame(tab, "打包选项")
        options_frame.pack(fill="x", padx=10, pady=10)
        
        options_container = self._create_container(options_frame)
        
        # 创建两列布局
        left_col = self._create_column(options_container)
        right_col = self._create_column(options_container)
        
        # 左列选项
        self.one_file_var = tk.BooleanVar(value=True)
        if USE_CTK:
            ctk.CTkCheckBox(
                left_col,
                text="打包成单个文件",
                variable=self.one_file_var
            ).pack(anchor="w", pady=3)
        else:
            ttk.Checkbutton(
                left_col,
                text="打包成单个文件",
                variable=self.one_file_var
            ).pack(anchor="w", pady=3)
        
        self.windowed_var = tk.BooleanVar(value=True)  # 默认隐藏控制台
        if USE_CTK:
            ctk.CTkCheckBox(
                left_col,
                text="窗口模式（隐藏控制台）",
                variable=self.windowed_var
            ).pack(anchor="w", pady=3)
        else:
            ttk.Checkbutton(
                left_col,
                text="窗口模式（隐藏控制台）",
                variable=self.windowed_var
            ).pack(anchor="w", pady=3)
        
        self.clean_var = tk.BooleanVar(value=True)
        if USE_CTK:
            ctk.CTkCheckBox(
                left_col,
                text="打包后清理临时文件",
                variable=self.clean_var
            ).pack(anchor="w", pady=3)
        else:
            ttk.Checkbutton(
                left_col,
                text="打包后清理临时文件",
                variable=self.clean_var
            ).pack(anchor="w", pady=3)
        
        # 右列选项
        self.admin_var = tk.BooleanVar(value=False)
        if USE_CTK:
            ctk.CTkCheckBox(
                right_col,
                text="需要管理员权限运行",
                variable=self.admin_var
            ).pack(anchor="w", pady=3)
        else:
            ttk.Checkbutton(
                right_col,
                text="需要管理员权限运行",
                variable=self.admin_var
            ).pack(anchor="w", pady=3)
        
        self.no_confirm_var = tk.BooleanVar(value=True)
        if USE_CTK:
            ctk.CTkCheckBox(
                right_col,
                text="覆盖时不确认",
                variable=self.no_confirm_var
            ).pack(anchor="w", pady=3)
        else:
            ttk.Checkbutton(
                right_col,
                text="覆盖时不确认",
                variable=self.no_confirm_var
            ).pack(anchor="w", pady=3)
            
        self.ascii_var = tk.BooleanVar(value=False)
        if USE_CTK:
            ctk.CTkCheckBox(
                right_col,
                text="使用ASCII编码（兼容性）",
                variable=self.ascii_var
            ).pack(anchor="w", pady=3)
        else:
            ttk.Checkbutton(
                right_col,
                text="使用ASCII编码（兼容性）",
                variable=self.ascii_var
            ).pack(anchor="w", pady=3)
    
    def _create_version_tab(self):
        """创建版本信息标签页"""
        tab = self._get_tab("版本信息")
        
        # 版本信息框架
        version_frame = self._create_frame(tab, "程序版本信息")
        version_frame.pack(fill="x", padx=10, pady=10)
        
        version_container = self._create_container(version_frame)
        
        # 文件版本
        file_ver_frame = self._create_row(version_container)
        if USE_CTK:
            ctk.CTkLabel(file_ver_frame, text="文件版本:", width=100).pack(side="left")
        else:
            ttk.Label(file_ver_frame, text="文件版本:", width=12).pack(side="left")
        
        self.file_version_var = tk.StringVar(value="1.0.0.0")
        if USE_CTK:
            ctk.CTkEntry(
                file_ver_frame,
                textvariable=self.file_version_var,
                placeholder_text="例如: 1.0.0.0"
            ).pack(side="left", fill="x", expand=True)
        else:
            ttk.Entry(file_ver_frame, textvariable=self.file_version_var).pack(side="left", fill="x", expand=True)
        
        # 产品版本
        prod_ver_frame = self._create_row(version_container)
        if USE_CTK:
            ctk.CTkLabel(prod_ver_frame, text="产品版本:", width=100).pack(side="left")
        else:
            ttk.Label(prod_ver_frame, text="产品版本:", width=12).pack(side="left")
        
        self.product_version_var = tk.StringVar(value="1.0.0")
        if USE_CTK:
            ctk.CTkEntry(
                prod_ver_frame,
                textvariable=self.product_version_var,
                placeholder_text="例如: 1.0.0"
            ).pack(side="left", fill="x", expand=True)
        else:
            ttk.Entry(prod_ver_frame, textvariable=self.product_version_var).pack(side="left", fill="x", expand=True)
        
        # 公司信息框架
        company_frame = self._create_frame(tab, "公司信息")
        company_frame.pack(fill="x", padx=10, pady=10)
        
        company_container = self._create_container(company_frame)
        
        # 公司名称
        company_name_frame = self._create_row(company_container)
        if USE_CTK:
            ctk.CTkLabel(company_name_frame, text="公司名称:", width=100).pack(side="left")
        else:
            ttk.Label(company_name_frame, text="公司名称:", width=12).pack(side="left")
        
        self.company_name_var = tk.StringVar()
        if USE_CTK:
            ctk.CTkEntry(
                company_name_frame,
                textvariable=self.company_name_var,
                placeholder_text="输入公司名称"
            ).pack(side="left", fill="x", expand=True)
        else:
            ttk.Entry(company_name_frame, textvariable=self.company_name_var).pack(side="left", fill="x", expand=True)
        
        # 产品名称
        product_name_frame = self._create_row(company_container)
        if USE_CTK:
            ctk.CTkLabel(product_name_frame, text="产品名称:", width=100).pack(side="left")
        else:
            ttk.Label(product_name_frame, text="产品名称:", width=12).pack(side="left")
        
        self.product_name_var = tk.StringVar()
        if USE_CTK:
            ctk.CTkEntry(
                product_name_frame,
                textvariable=self.product_name_var,
                placeholder_text="输入产品名称"
            ).pack(side="left", fill="x", expand=True)
        else:
            ttk.Entry(product_name_frame, textvariable=self.product_name_var).pack(side="left", fill="x", expand=True)
        
        # 版权和描述框架
        desc_frame = self._create_frame(tab, "版权和描述")
        desc_frame.pack(fill="x", padx=10, pady=10)
        
        desc_container = self._create_container(desc_frame)
        
        # 版权信息
        copyright_frame = self._create_row(desc_container)
        if USE_CTK:
            ctk.CTkLabel(copyright_frame, text="版权信息:", width=100).pack(side="left")
        else:
            ttk.Label(copyright_frame, text="版权信息:", width=12).pack(side="left")
        
        current_year = datetime.now().year
        self.copyright_var = tk.StringVar(value=f"Copyright © {current_year}")
        if USE_CTK:
            ctk.CTkEntry(
                copyright_frame,
                textvariable=self.copyright_var,
                placeholder_text="例如: Copyright © 2024 Company"
            ).pack(side="left", fill="x", expand=True)
        else:
            ttk.Entry(copyright_frame, textvariable=self.copyright_var).pack(side="left", fill="x", expand=True)
        
        # 文件描述
        desc_label_frame = self._create_row(desc_container)
        if USE_CTK:
            ctk.CTkLabel(desc_label_frame, text="文件描述:", width=100).pack(side="left", anchor="n")
        else:
            ttk.Label(desc_label_frame, text="文件描述:", width=12).pack(side="left", anchor="n")
        
        self.file_description_var = tk.StringVar()
        if USE_CTK:
            self.file_desc_text = ctk.CTkTextbox(
                desc_label_frame,
                height=80,
                font=ctk.CTkFont(size=12)
            )
        else:
            text_frame = ttk.Frame(desc_label_frame)
            text_frame.pack(side="left", fill="x", expand=True)
            scrollbar = ttk.Scrollbar(text_frame)
            scrollbar.pack(side="right", fill="y")
            self.file_desc_text = tk.Text(
                text_frame,
                height=4,
                font=("", 11),
                yscrollcommand=scrollbar.set
            )
            scrollbar.config(command=self.file_desc_text.yview)
        self.file_desc_text.pack(side="left", fill="x", expand=True)
    
    def _create_advanced_tab(self):
        """创建高级选项标签页"""
        tab = self._get_tab("高级选项")
        
        # 性能优化框架
        perf_frame = self._create_frame(tab, "性能优化")
        perf_frame.pack(fill="x", padx=10, pady=10)
        
        perf_container = self._create_container(perf_frame)
        
        # UPX压缩
        upx_frame = self._create_row(perf_container)
        self.use_upx_var = tk.BooleanVar(value=False)
        if USE_CTK:
            ctk.CTkCheckBox(
                upx_frame,
                text="使用UPX压缩（减小文件体积）",
                variable=self.use_upx_var,
                command=self._toggle_upx
            ).pack(side="left")
        else:
            ttk.Checkbutton(
                upx_frame,
                text="使用UPX压缩（减小文件体积）",
                variable=self.use_upx_var,
                command=self._toggle_upx
            ).pack(side="left")
        
        # UPX路径
        self.upx_dir_frame = self._create_row(perf_container)
        if USE_CTK:
            ctk.CTkLabel(self.upx_dir_frame, text="UPX路径:", width=100).pack(side="left")
        else:
            ttk.Label(self.upx_dir_frame, text="UPX路径:", width=12).pack(side="left")
        
        self.upx_dir_var = tk.StringVar()
        if USE_CTK:
            ctk.CTkEntry(
                self.upx_dir_frame,
                textvariable=self.upx_dir_var,
                placeholder_text="UPX程序目录（可留空自动搜索）"
            ).pack(side="left", fill="x", expand=True, padx=(0, 10))
            
            ctk.CTkButton(
                self.upx_dir_frame,
                text="浏览",
                command=self._select_upx_dir,
                width=80
            ).pack(side="left")
        else:
            ttk.Entry(self.upx_dir_frame, textvariable=self.upx_dir_var).pack(side="left", fill="x", expand=True, padx=(0, 10))
            ttk.Button(self.upx_dir_frame, text="浏览", command=self._select_upx_dir).pack(side="left")
        
        self.upx_dir_frame.pack_forget()  # 默认隐藏
        
        # Python优化级别
        opt_frame = self._create_row(perf_container)
        if USE_CTK:
            ctk.CTkLabel(opt_frame, text="优化级别:", width=100).pack(side="left")
        else:
            ttk.Label(opt_frame, text="优化级别:", width=12).pack(side="left")
        
        self.optimize_var = tk.StringVar(value="0")
        if USE_CTK:
            ctk.CTkSegmentedButton(
                opt_frame,
                values=["0 (无优化)", "1 (基本)", "2 (最大)"],
                variable=self.optimize_var
            ).pack(side="left", fill="x", expand=True)
        else:
            opt_combo = ttk.Combobox(
                opt_frame,
                textvariable=self.optimize_var,
                values=["0", "1", "2"],
                state="readonly",
                width=15
            )
            opt_combo.pack(side="left")
        
        # 导入管理框架
        import_frame = self._create_frame(tab, "导入管理")
        import_frame.pack(fill="x", padx=10, pady=10)
        
        import_container = self._create_container(import_frame)
        
        # 隐藏导入
        hidden_frame = self._create_row(import_container)
        if USE_CTK:
            ctk.CTkLabel(hidden_frame, text="隐藏导入:", width=100).pack(side="left", anchor="n")
        else:
            ttk.Label(hidden_frame, text="隐藏导入:", width=12).pack(side="left", anchor="n")
        
        hidden_list_frame = tk.Frame(hidden_frame)
        hidden_list_frame.pack(side="left", fill="x", expand=True)
        
        # 列表框和滚动条
        scrollbar = ttk.Scrollbar(hidden_list_frame)
        scrollbar.pack(side="right", fill="y")
        
        self.hidden_imports_listbox = tk.Listbox(
            hidden_list_frame,
            height=4,
            selectmode=tk.EXTENDED,
            yscrollcommand=scrollbar.set
        )
        self.hidden_imports_listbox.pack(side="left", fill="both", expand=True)
        scrollbar.config(command=self.hidden_imports_listbox.yview)
        
        # 按钮
        hidden_btn_frame = tk.Frame(hidden_frame)
        hidden_btn_frame.pack(side="left", padx=(10, 0))
        
        if USE_CTK:
            ctk.CTkButton(
                hidden_btn_frame,
                text="添加",
                command=self._add_hidden_import,
                width=60,
                height=28
            ).pack(pady=2)
            
            ctk.CTkButton(
                hidden_btn_frame,
                text="删除",
                command=self._remove_hidden_import,
                width=60,
                height=28,
                fg_color=COLORS["danger"]
            ).pack(pady=2)
        else:
            ttk.Button(hidden_btn_frame, text="添加", command=self._add_hidden_import).pack(pady=2)
            ttk.Button(hidden_btn_frame, text="删除", command=self._remove_hidden_import).pack(pady=2)
        
        # 排除模块
        exclude_frame = self._create_row(import_container)
        if USE_CTK:
            ctk.CTkLabel(exclude_frame, text="排除模块:", width=100).pack(side="left", anchor="n")
        else:
            ttk.Label(exclude_frame, text="排除模块:", width=12).pack(side="left", anchor="n")
        
        exclude_list_frame = tk.Frame(exclude_frame)
        exclude_list_frame.pack(side="left", fill="x", expand=True)
        
        # 列表框和滚动条
        scrollbar2 = ttk.Scrollbar(exclude_list_frame)
        scrollbar2.pack(side="right", fill="y")
        
        self.exclude_modules_listbox = tk.Listbox(
            exclude_list_frame,
            height=4,
            selectmode=tk.EXTENDED,
            yscrollcommand=scrollbar2.set
        )
        self.exclude_modules_listbox.pack(side="left", fill="both", expand=True)
        scrollbar2.config(command=self.exclude_modules_listbox.yview)
        
        # 初始化默认排除模块
        for module in self.exclude_modules:
            self.exclude_modules_listbox.insert(tk.END, module)
        
        # 按钮
        exclude_btn_frame = tk.Frame(exclude_frame)
        exclude_btn_frame.pack(side="left", padx=(10, 0))
        
        if USE_CTK:
            ctk.CTkButton(
                exclude_btn_frame,
                text="添加",
                command=self._add_exclude_module,
                width=60,
                height=28
            ).pack(pady=2)
            
            ctk.CTkButton(
                exclude_btn_frame,
                text="删除",
                command=self._remove_exclude_module,
                width=60,
                height=28,
                fg_color=COLORS["danger"]
            ).pack(pady=2)
        else:
            ttk.Button(exclude_btn_frame, text="添加", command=self._add_exclude_module).pack(pady=2)
            ttk.Button(exclude_btn_frame, text="删除", command=self._remove_exclude_module).pack(pady=2)
        
        # 调试选项框架
        debug_frame = self._create_frame(tab, "调试选项")
        debug_frame.pack(fill="x", padx=10, pady=10)
        
        debug_container = self._create_container(debug_frame)
        
        # 调试选项
        self.debug_var = tk.BooleanVar(value=False)
        if USE_CTK:
            ctk.CTkCheckBox(
                debug_container,
                text="启用调试模式（显示详细信息）",
                variable=self.debug_var
            ).pack(anchor="w", pady=3)
        else:
            ttk.Checkbutton(
                debug_container,
                text="启用调试模式（显示详细信息）",
                variable=self.debug_var
            ).pack(anchor="w", pady=3)
        
        # 日志级别
        log_frame = self._create_row(debug_container)
        if USE_CTK:
            ctk.CTkLabel(log_frame, text="日志级别:", width=100).pack(side="left")
        else:
            ttk.Label(log_frame, text="日志级别:", width=12).pack(side="left")
        
        self.log_level_var = tk.StringVar(value="INFO")
        if USE_CTK:
            ctk.CTkSegmentedButton(
                log_frame,
                values=["TRACE", "DEBUG", "INFO", "WARN", "ERROR"],
                variable=self.log_level_var
            ).pack(side="left")
        else:
            log_combo = ttk.Combobox(
                log_frame,
                textvariable=self.log_level_var,
                values=["TRACE", "DEBUG", "INFO", "WARN", "ERROR"],
                state="readonly",
                width=15
            )
            log_combo.pack(side="left")
            
    def _create_resource_tab(self):
        """创建附加资源标签页"""
        tab = self._get_tab("附加资源")
        
        # 图标设置
        icon_frame = self._create_frame(tab, "应用图标")
        icon_frame.pack(fill="x", padx=10, pady=10)
        
        icon_container = self._create_container(icon_frame)
        
        self.icon_path_var = tk.StringVar()
        if USE_CTK:
            icon_entry_frame = ctk.CTkFrame(icon_container, fg_color="transparent")
        else:
            icon_entry_frame = ttk.Frame(icon_container)
        icon_entry_frame.pack(fill="x")
        
        if USE_CTK:
            ctk.CTkEntry(
                icon_entry_frame,
                textvariable=self.icon_path_var,
                placeholder_text="选择图标文件（支持所有图片格式）"
            ).pack(side="left", fill="x", expand=True, padx=(0, 10))
            
            ctk.CTkButton(
                icon_entry_frame,
                text="选择图标",
                command=self._select_icon,
                width=100
            ).pack(side="left")
        else:
            ttk.Entry(icon_entry_frame, textvariable=self.icon_path_var).pack(side="left", fill="x", expand=True, padx=(0, 10))
            ttk.Button(icon_entry_frame, text="选择图标", command=self._select_icon).pack(side="left")
        
        # 图标转换提示
        if USE_CTK:
            icon_tip = ctk.CTkLabel(
                icon_container,
                text="✨ 支持自动转换JPG、PNG、BMP等格式为ICO",
                font=ctk.CTkFont(size=11),
                text_color="gray"
            )
        else:
            icon_tip = ttk.Label(
                icon_container,
                text="✨ 支持自动转换JPG、PNG、BMP等格式为ICO",
                font=("", 9)
            )
        icon_tip.pack(anchor="w", pady=(5, 0))
        
        # 附加文件
        files_frame = self._create_frame(tab, "附加文件和文件夹")
        files_frame.pack(fill="both", expand=True, padx=10, pady=10)
        
        files_container = self._create_container(files_frame)
        
        # 文件列表
        list_frame = tk.Frame(files_container)
        list_frame.pack(fill="both", expand=True, pady=(0, 10))
        
        scrollbar = ttk.Scrollbar(list_frame)
        scrollbar.pack(side="right", fill="y")
        
        self.files_listbox = tk.Listbox(
            list_frame,
            height=10,
            selectmode=tk.EXTENDED,
            yscrollcommand=scrollbar.set
        )
        self.files_listbox.pack(side="left", fill="both", expand=True)
        scrollbar.config(command=self.files_listbox.yview)
        
        # 文件操作按钮
        if USE_CTK:
            btn_frame = ctk.CTkFrame(files_container, fg_color="transparent")
        else:
            btn_frame = ttk.Frame(files_container)
        btn_frame.pack(fill="x")
        
        if USE_CTK:
            ctk.CTkButton(
                btn_frame,
                text="添加文件",
                command=self._add_file,
                width=100
            ).pack(side="left", padx=(0, 5))
            
            ctk.CTkButton(
                btn_frame,
                text="添加文件夹",
                command=self._add_folder,
                width=100
            ).pack(side="left", padx=(0, 5))
            
            ctk.CTkButton(
                btn_frame,
                text="删除选中",
                command=self._remove_files,
                width=100,
                fg_color=COLORS["danger"]
            ).pack(side="left", padx=(0, 5))
            
            ctk.CTkButton(
                btn_frame,
                text="清空列表",
                command=self._clear_files,
                width=100,
                fg_color=COLORS["warning"]
            ).pack(side="left")
        else:
            ttk.Button(btn_frame, text="添加文件", command=self._add_file).pack(side="left", padx=(0, 5))
            ttk.Button(btn_frame, text="添加文件夹", command=self._add_folder).pack(side="left", padx=(0, 5))
            ttk.Button(btn_frame, text="删除选中", command=self._remove_files).pack(side="left", padx=(0, 5))
            ttk.Button(btn_frame, text="清空列表", command=self._clear_files).pack(side="left")
            
        # 签名选项框架（Windows专用）
        if sys.platform == "win32":
            sign_frame = self._create_frame(tab, "代码签名（可选）")
            sign_frame.pack(fill="x", padx=10, pady=10)
            
            sign_container = self._create_container(sign_frame)
            
            self.sign_var = tk.BooleanVar(value=False)
            if USE_CTK:
                ctk.CTkCheckBox(
                    sign_container,
                    text="对生成的exe进行数字签名",
                    variable=self.sign_var,
                    command=self._toggle_sign
                ).pack(anchor="w", pady=3)
            else:
                ttk.Checkbutton(
                    sign_container,
                    text="对生成的exe进行数字签名",
                    variable=self.sign_var,
                    command=self._toggle_sign
                ).pack(anchor="w", pady=3)
            
            # 证书路径
            self.cert_frame = self._create_row(sign_container)
            if USE_CTK:
                ctk.CTkLabel(self.cert_frame, text="证书文件:", width=100).pack(side="left")
            else:
                ttk.Label(self.cert_frame, text="证书文件:", width=12).pack(side="left")
            
            self.cert_path_var = tk.StringVar()
            if USE_CTK:
                ctk.CTkEntry(
                    self.cert_frame,
                    textvariable=self.cert_path_var,
                    placeholder_text="选择证书文件(.pfx/.p12)"
                ).pack(side="left", fill="x", expand=True, padx=(0, 10))
                
                ctk.CTkButton(
                    self.cert_frame,
                    text="浏览",
                    command=self._select_cert,
                    width=80
                ).pack(side="left")
            else:
                ttk.Entry(self.cert_frame, textvariable=self.cert_path_var).pack(side="left", fill="x", expand=True, padx=(0, 10))
                ttk.Button(self.cert_frame, text="浏览", command=self._select_cert).pack(side="left")
            
            self.cert_frame.pack_forget()  # 默认隐藏
        
    def _create_output_tab(self):
        """创建输出标签页"""
        tab = self._get_tab("打包输出")
        
        # 进度条
        if USE_CTK:
            progress_frame = ctk.CTkFrame(tab, fg_color="transparent")
        else:
            progress_frame = ttk.Frame(tab)
        progress_frame.pack(fill="x", padx=10, pady=(10, 5))
        
        if USE_CTK:
            self.progress_label = ctk.CTkLabel(
                progress_frame,
                text="准备就绪",
                font=ctk.CTkFont(size=13, weight="bold")
            )
        else:
            self.progress_label = ttk.Label(
                progress_frame,
                text="准备就绪",
                font=("", 12, "bold")
            )
        self.progress_label.pack(anchor="w", pady=(0, 5))
        
        if USE_CTK:
            self.progress_bar = ctk.CTkProgressBar(
                progress_frame,
                height=20,
                corner_radius=10,
                progress_color=COLORS["success"]
            )
            self.progress_bar.pack(fill="x", pady=(0, 10))
            self.progress_bar.set(0)
        else:
            self.progress_bar = ttk.Progressbar(
                progress_frame,
                mode='determinate'
            )
            self.progress_bar.pack(fill="x", pady=(0, 10))
            self.progress_bar['value'] = 0
        
        # 输出文本
        if USE_CTK:
            self.output_text = ctk.CTkTextbox(
                tab,
                font=ctk.CTkFont(family="Consolas", size=11)
            )
        else:
            output_container = ttk.Frame(tab)
            output_container.pack(fill="both", expand=True, padx=10, pady=(0, 10))
            
            scrollbar = ttk.Scrollbar(output_container)
            scrollbar.pack(side="right", fill="y")
            
            self.output_text = tk.Text(
                output_container,
                font=("Consolas", 11),
                yscrollcommand=scrollbar.set
            )
            scrollbar.config(command=self.output_text.yview)
        
        self.output_text.pack(fill="both", expand=True, padx=10, pady=(0, 10))
        
    def _create_bottom_buttons(self):
        """创建底部按钮"""
        if USE_CTK:
            button_frame = ctk.CTkFrame(self, height=70, fg_color=COLORS["secondary"], corner_radius=10)
        else:
            button_frame = ttk.Frame(self)
        button_frame.pack(fill="x", padx=10, pady=(0, 10))
        button_frame.pack_propagate(False)
        
        # 左侧按钮
        if USE_CTK:
            left_frame = ctk.CTkFrame(button_frame, fg_color="transparent")
        else:
            left_frame = ttk.Frame(button_frame)
        left_frame.pack(side="left", fill="y", padx=15, pady=15)
        
        if USE_CTK:
            ctk.CTkButton(
                left_frame,
                text="保存配置",
                command=self._save_config,
                width=100,
                height=40
            ).pack(side="left", padx=(0, 8))
            
            ctk.CTkButton(
                left_frame,
                text="加载配置",
                command=self._load_config,
                width=100,
                height=40
            ).pack(side="left", padx=(0, 8))
            
            ctk.CTkButton(
                left_frame,
                text="重置设置",
                command=self._reset_settings,
                width=100,
                height=40,
                fg_color=COLORS["warning"]
            ).pack(side="left")
        else:
            ttk.Button(left_frame, text="保存配置", command=self._save_config).pack(side="left", padx=(0, 8))
            ttk.Button(left_frame, text="加载配置", command=self._load_config).pack(side="left", padx=(0, 8))
            ttk.Button(left_frame, text="重置设置", command=self._reset_settings).pack(side="left")
        
        # 右侧按钮
        if USE_CTK:
            right_frame = ctk.CTkFrame(button_frame, fg_color="transparent")
        else:
            right_frame = ttk.Frame(button_frame)
        right_frame.pack(side="right", fill="y", padx=15, pady=15)
        
        if USE_CTK:
            ctk.CTkButton(
                right_frame,
                text="生成命令",
                command=self._generate_command,
                width=120,
                height=40,
                fg_color=COLORS["info"]
            ).pack(side="left", padx=(0, 8))
            
            self.pack_button = ctk.CTkButton(
                right_frame,
                text="开始打包",
                command=self._start_packing,
                width=150,
                height=40,
                fg_color=COLORS["success"],
                font=ctk.CTkFont(size=15, weight="bold")
            )
        else:
            ttk.Button(
                right_frame,
                text="生成命令",
                command=self._generate_command
            ).pack(side="left", padx=(0, 8))
            
            self.pack_button = ttk.Button(
                right_frame,
                text="开始打包",
                command=self._start_packing
            )
        self.pack_button.pack(side="left")
        
    # 辅助方法
    def _create_frame(self, parent, title):
        """创建带标题的框架"""
        if USE_CTK:
            frame = ctk.CTkFrame(parent, corner_radius=10)
            if title:
                label = ctk.CTkLabel(
                    frame,
                    text=title,
                    font=ctk.CTkFont(size=14, weight="bold")
                )
                label.pack(anchor="w", padx=15, pady=(10, 5))
        else:
            frame = ttk.LabelFrame(parent, text=title, padding=10)
        return frame
    
    def _create_container(self, parent):
        """创建内容容器"""
        if USE_CTK:
            container = ctk.CTkFrame(parent, fg_color="transparent")
        else:
            container = ttk.Frame(parent)
        container.pack(fill="x", padx=20, pady=(0, 15))
        return container
    
    def _create_row(self, parent):
        """创建行容器"""
        if USE_CTK:
            row = ctk.CTkFrame(parent, fg_color="transparent")
        else:
            row = ttk.Frame(parent)
        row.pack(fill="x", pady=5)
        return row
    
    def _create_column(self, parent):
        """创建列容器"""
        if USE_CTK:
            col = ctk.CTkFrame(parent, fg_color="transparent")
        else:
            col = ttk.Frame(parent)
        col.pack(side="left", fill="both", expand=True, padx=5)
        return col
        
    def _check_environment(self):
        """检查环境"""
        self._output("=== 向日葵Python打包程序 v3.0 ===\n")
        self._output("=== 环境检查 ===\n")
        self._output(f"Python版本: {sys.version}\n")
        self._output(f"操作系统: {os.name}\n")
        self._output(f"平台: {sys.platform}\n")
        
        # 检查Pillow
        if PIL_AVAILABLE:
            self._output("Pillow: 已安装（支持图片格式转换）\n")
        else:
            self._output("Pillow: 未安装（建议安装以支持更多图片格式）\n")
            self._output("安装命令: pip install Pillow\n")
        
        # 检查UPX（可选）
        try:
            result = subprocess.run(
                ["upx", "--version"],
                capture_output=True,
                text=True,
                timeout=5
            )
            if result.returncode == 0:
                version_line = result.stdout.split('\n')[0]
                self._output(f"UPX: {version_line}\n")
        except:
            self._output("UPX: 未安装（可选，用于压缩）\n")
        
        self._output("\n准备就绪！\n")
        
    # 文件选择方法
    def _select_script(self):
        """选择Python文件"""
        filename = filedialog.askopenfilename(
            title="选择Python文件",
            filetypes=[("Python文件", "*.py;*.pyw"), ("所有文件", "*.*")]
        )
        if filename:
            self.script_path_var.set(filename)
            if not self.output_dir_var.get():
                self.output_dir_var.set(os.path.dirname(filename))
            if not self.app_name_var.get():
                name = os.path.splitext(os.path.basename(filename))[0]
                self.app_name_var.set(name)
            if not self.product_name_var.get():
                self.product_name_var.set(name)
                
    def _select_output_dir(self):
        """选择输出目录"""
        folder = filedialog.askdirectory(title="选择输出目录")
        if folder:
            self.output_dir_var.set(folder)
            
    def _select_icon(self):
        """选择图标（支持多种格式）"""
        filename = filedialog.askopenfilename(
            title="选择图标文件",
            filetypes=[
                ("所有图片", "*.ico;*.png;*.jpg;*.jpeg;*.bmp;*.gif;*.tiff"),
                ("ICO文件", "*.ico"),
                ("PNG文件", "*.png"),
                ("JPG文件", "*.jpg;*.jpeg"),
                ("BMP文件", "*.bmp"),
                ("所有文件", "*.*")
            ]
        )
        if filename:
            # 如果不是ICO格式，转换为ICO
            if not filename.lower().endswith('.ico'):
                ico_path = self._convert_to_ico(filename)
                if ico_path:
                    self.icon_path_var.set(ico_path)
                    self._output(f"[INFO] 图标已转换为ICO格式: {ico_path}\n")
                else:
                    self.icon_path_var.set(filename)
                    self._output(f"[WARNING] 无法转换图标，将尝试使用原文件\n")
            else:
                self.icon_path_var.set(filename)
    
    def _convert_to_ico(self, image_path):
        """将图片转换为ICO格式"""
        if not PIL_AVAILABLE:
            messagebox.showwarning(
                "警告",
                "Pillow未安装，无法转换图片格式。\n请运行: pip install Pillow"
            )
            return None
        
        try:
            # 打开图片
            img = Image.open(image_path)
            
            # 转换为RGBA（如果需要）
            if img.mode != 'RGBA':
                img = img.convert('RGBA')
            
            # 创建ICO文件路径（使用输出目录）
            ico_dir = self.output_dir_var.get()
            if not ico_dir:
                ico_dir = os.path.dirname(image_path)
            ico_name = os.path.splitext(os.path.basename(image_path))[0] + '.ico'
            ico_path = os.path.join(ico_dir, ico_name)
            
            # 生成单个大尺寸图标（256x256），以避免小尺寸压缩画质
            sizes = [(256, 256)]
            imgs = []
            
            for size in sizes:
                # 创建缩略图
                resized = img.copy()
                resized.thumbnail(size, Image.Resampling.LANCZOS)
                
                # 创建新的正方形图像
                new_img = Image.new('RGBA', size, (0, 0, 0, 0))
                
                # 将缩略图粘贴到中心
                x = (size[0] - resized.width) // 2
                y = (size[1] - resized.height) // 2
                new_img.paste(resized, (x, y))
                
                imgs.append(new_img)
            
            # 保存为ICO
            imgs[0].save(ico_path, format='ICO', sizes=[(s.width, s.height) for s in imgs])
            
            return ico_path
        except Exception as e:
            self._output(f"[ERROR] 图标转换失败: {str(e)}\n")
            return None
    
    def _select_upx_dir(self):
        """选择UPX目录"""
        folder = filedialog.askdirectory(title="选择UPX程序目录")
        if folder:
            self.upx_dir_var.set(folder)
    
    def _select_cert(self):
        """选择证书文件"""
        filename = filedialog.askopenfilename(
            title="选择证书文件",
            filetypes=[("证书文件", "*.pfx;*.p12"), ("所有文件", "*.*")]
        )
        if filename:
            self.cert_path_var.set(filename)
            
    def _add_file(self):
        """添加文件"""
        filenames = filedialog.askopenfilenames(title="选择要添加的文件")
        for filename in filenames:
            if filename not in self.files_listbox.get(0, tk.END):
                self.files_listbox.insert(tk.END, filename)
                
    def _add_folder(self):
        """添加文件夹"""
        folder = filedialog.askdirectory(title="选择要添加的文件夹")
        if folder and folder not in self.files_listbox.get(0, tk.END):
            self.files_listbox.insert(tk.END, folder)
            
    def _remove_files(self):
        """删除选中文件"""
        selection = self.files_listbox.curselection()
        for index in reversed(selection):
            self.files_listbox.delete(index)
    
    def _clear_files(self):
        """清空文件列表"""
        self.files_listbox.delete(0, tk.END)
    
    # 高级选项相关方法
    def _toggle_upx(self):
        """切换UPX选项"""
        if self.use_upx_var.get():
            self.upx_dir_frame.pack(fill="x", pady=5)
        else:
            self.upx_dir_frame.pack_forget()
    
    def _toggle_sign(self):
        """切换签名选项"""
        if self.sign_var.get():
            self.cert_frame.pack(fill="x", pady=5)
        else:
            self.cert_frame.pack_forget()
    
    def _add_hidden_import(self):
        """添加隐藏导入"""
        dialog = tk.Toplevel(self)
        dialog.title("添加隐藏导入")
        dialog.geometry("400x150")
        dialog.transient(self)
        dialog.grab_set()
        
        ttk.Label(dialog, text="输入模块名称:").pack(pady=10)
        
        entry = ttk.Entry(dialog, width=40)
        entry.pack(pady=5)
        entry.focus()
        
        def add():
            module = entry.get().strip()
            if module and module not in self.hidden_imports:
                self.hidden_imports.append(module)
                self.hidden_imports_listbox.insert(tk.END, module)
            dialog.destroy()
        
        ttk.Button(dialog, text="添加", command=add).pack(pady=10)
        
    def _remove_hidden_import(self):
        """删除隐藏导入"""
        selection = self.hidden_imports_listbox.curselection()
        for index in reversed(selection):
            module = self.hidden_imports_listbox.get(index)
            self.hidden_imports.remove(module)
            self.hidden_imports_listbox.delete(index)
    
    def _add_exclude_module(self):
        """添加排除模块"""
        dialog = tk.Toplevel(self)
        dialog.title("添加排除模块")
        dialog.geometry("400x150")
        dialog.transient(self)
        dialog.grab_set()
        
        ttk.Label(dialog, text="输入模块名称:").pack(pady=10)
        
        entry = ttk.Entry(dialog, width=40)
        entry.pack(pady=5)
        entry.focus()
        
        def add():
            module = entry.get().strip()
            if module and module not in self.exclude_modules:
                # 确保不排除样式相关模块
                if module not in ['tkinter', 'ttk', 'customtkinter', 'ctk']:
                    self.exclude_modules.append(module)
                    self.exclude_modules_listbox.insert(tk.END, module)
                else:
                    messagebox.showwarning("警告", f"不建议排除 {module} 模块，这可能影响程序界面")
            dialog.destroy()
        
        ttk.Button(dialog, text="添加", command=add).pack(pady=10)
        
    def _remove_exclude_module(self):
        """删除排除模块"""
        selection = self.exclude_modules_listbox.curselection()
        for index in reversed(selection):
            module = self.exclude_modules_listbox.get(index)
            self.exclude_modules.remove(module)
            self.exclude_modules_listbox.delete(index)
    
    # 配置管理
    def _save_config(self):
        """保存配置"""
        filename = filedialog.asksaveasfilename(
            title="保存配置",
            defaultextension=".json",
            filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")]
        )
        if not filename:
            return
        
        config = {
            # 基本设置
            "script_path": self.script_path_var.get(),
            "output_dir": self.output_dir_var.get(),
            "app_name": self.app_name_var.get(),
            "icon_path": self.icon_path_var.get(),
            "one_file": self.one_file_var.get(),
            "windowed": self.windowed_var.get(),
            "clean_build": self.clean_var.get(),
            "admin": self.admin_var.get(),
            "no_confirm": self.no_confirm_var.get(),
            "ascii": self.ascii_var.get(),
            
            # 版本信息
            "file_version": self.file_version_var.get(),
            "product_version": self.product_version_var.get(),
            "company_name": self.company_name_var.get(),
            "product_name": self.product_name_var.get(),
            "copyright": self.copyright_var.get(),
            "file_description": self.file_desc_text.get("1.0", "end-1c") if hasattr(self, 'file_desc_text') else "",
            
            # 高级选项
            "use_upx": self.use_upx_var.get(),
            "upx_dir": self.upx_dir_var.get(),
            "optimize": self.optimize_var.get(),
            "hidden_imports": self.hidden_imports,
            "exclude_modules": self.exclude_modules,
            "debug": self.debug_var.get(),
            "log_level": self.log_level_var.get(),
            
            # 附加资源
            "additional_files": list(self.files_listbox.get(0, tk.END)),
        }
        
        # Windows特有选项
        if sys.platform == "win32":
            config["sign"] = self.sign_var.get()
            config["cert_path"] = self.cert_path_var.get()
        
        try:
            with open(filename, 'w', encoding='utf-8') as f:
                json.dump(config, f, ensure_ascii=False, indent=2)
            messagebox.showinfo("成功", "配置已保存")
        except Exception as e:
            messagebox.showerror("错误", f"保存失败: {str(e)}")
            
    def _load_config(self):
        """加载配置"""
        filename = filedialog.askopenfilename(
            title="加载配置",
            filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")]
        )
        if not filename:
            return
        
        try:
            with open(filename, 'r', encoding='utf-8') as f:
                config = json.load(f)
            
            # 基本设置
            self.script_path_var.set(config.get("script_path", ""))
            self.output_dir_var.set(config.get("output_dir", ""))
            self.app_name_var.set(config.get("app_name", ""))
            self.icon_path_var.set(config.get("icon_path", ""))
            self.one_file_var.set(config.get("one_file", True))
            self.windowed_var.set(config.get("windowed", True))
            self.clean_var.set(config.get("clean_build", True))
            self.admin_var.set(config.get("admin", False))
            self.no_confirm_var.set(config.get("no_confirm", True))
            self.ascii_var.set(config.get("ascii", False))
            
            # 版本信息
            self.file_version_var.set(config.get("file_version", "1.0.0.0"))
            self.product_version_var.set(config.get("product_version", "1.0.0"))
            self.company_name_var.set(config.get("company_name", ""))
            self.product_name_var.set(config.get("product_name", ""))
            self.copyright_var.set(config.get("copyright", ""))
            
            if hasattr(self, 'file_desc_text'):
                self.file_desc_text.delete("1.0", "end")
                self.file_desc_text.insert("1.0", config.get("file_description", ""))
            
            # 高级选项
            self.use_upx_var.set(config.get("use_upx", False))
            self.upx_dir_var.set(config.get("upx_dir", ""))
            self.optimize_var.set(config.get("optimize", "0"))
            self.debug_var.set(config.get("debug", False))
            self.log_level_var.set(config.get("log_level", "INFO"))
            
            # 隐藏导入
            self.hidden_imports = config.get("hidden_imports", [])
            self.hidden_imports_listbox.delete(0, tk.END)
            for module in self.hidden_imports:
                self.hidden_imports_listbox.insert(tk.END, module)
            
            # 排除模块
            self.exclude_modules = config.get("exclude_modules", DEFAULT_EXCLUDE_MODULES.copy())
            self.exclude_modules_listbox.delete(0, tk.END)
            for module in self.exclude_modules:
                self.exclude_modules_listbox.insert(tk.END, module)
            
            # 附加文件
            self.files_listbox.delete(0, tk.END)
            for file in config.get("additional_files", []):
                self.files_listbox.insert(tk.END, file)
            
            # Windows特有选项
            if sys.platform == "win32":
                self.sign_var.set(config.get("sign", False))
                self.cert_path_var.set(config.get("cert_path", ""))
            
            # 更新UI状态
            self._toggle_upx()
            if sys.platform == "win32":
                self._toggle_sign()
            
            messagebox.showinfo("成功", "配置已加载")
        except Exception as e:
            messagebox.showerror("错误", f"加载失败: {str(e)}")
    
    def _reset_settings(self):
        """重置设置"""
        if messagebox.askyesno("确认", "确定要重置所有设置吗？"):
            # 基本设置
            self.script_path_var.set("")
            self.output_dir_var.set("")
            self.app_name_var.set("")
            self.icon_path_var.set("")
            self.one_file_var.set(True)
            self.windowed_var.set(True)  # 默认隐藏控制台
            self.clean_var.set(True)
            self.admin_var.set(False)
            self.no_confirm_var.set(True)
            self.ascii_var.set(False)
            
            # 版本信息
            self.file_version_var.set("1.0.0.0")
            self.product_version_var.set("1.0.0")
            self.company_name_var.set("")
            self.product_name_var.set("")
            self.copyright_var.set(f"Copyright © {datetime.now().year}")
            
            if hasattr(self, 'file_desc_text'):
                self.file_desc_text.delete("1.0", "end")
            
            # 高级选项
            self.use_upx_var.set(False)
            self.upx_dir_var.set("")
            self.optimize_var.set("0")
            self.debug_var.set(False)
            self.log_level_var.set("INFO")
            
            # 清空列表
            self.hidden_imports = []
            self.hidden_imports_listbox.delete(0, tk.END)
            self.exclude_modules = DEFAULT_EXCLUDE_MODULES.copy()
            self.exclude_modules_listbox.delete(0, tk.END)
            for module in self.exclude_modules:
                self.exclude_modules_listbox.insert(tk.END, module)
            self.files_listbox.delete(0, tk.END)
            
            # Windows特有选项
            if sys.platform == "win32":
                self.sign_var.set(False)
                self.cert_path_var.set("")
            
            # 更新UI状态
            self._toggle_upx()
            if sys.platform == "win32":
                self._toggle_sign()
            
            messagebox.showinfo("成功", "设置已重置")
    
    def _generate_command(self):
        """生成PyInstaller命令"""
        if not self.script_path_var.get():
            messagebox.showwarning("警告", "请先选择Python文件")
            return
        
        cmd = self._build_command()
        
        # 显示命令
        dialog = tk.Toplevel(self)
        dialog.title("生成的命令")
        dialog.geometry("800x400")
        dialog.transient(self)
        
        ttk.Label(dialog, text="生成的PyInstaller命令:", font=("", 12, "bold")).pack(pady=10)
        
        text_frame = ttk.Frame(dialog)
        text_frame.pack(fill="both", expand=True, padx=20, pady=(0, 20))
        
        scrollbar = ttk.Scrollbar(text_frame)
        scrollbar.pack(side="right", fill="y")
        
        text = tk.Text(text_frame, wrap="word", yscrollcommand=scrollbar.set)
        text.pack(side="left", fill="both", expand=True)
        scrollbar.config(command=text.yview)
        
        # 插入命令
        cmd_str = " ".join(cmd)
        text.insert("1.0", cmd_str)
        
        # 复制按钮
        def copy_command():
            self.clipboard_clear()
            self.clipboard_append(cmd_str)
            messagebox.showinfo("成功", "命令已复制到剪贴板")
        
        ttk.Button(dialog, text="复制命令", command=copy_command).pack(pady=10)
    
    def _build_command(self):
        """构建PyInstaller命令"""
        cmd = ["pyinstaller"]
        
        # 基本参数
        if self.one_file_var.get():
            cmd.append("--onefile")
        else:
            cmd.append("--onedir")
        
        if self.windowed_var.get():
            cmd.append("--windowed")
        else:
            cmd.append("--console")
        
        # 管理员权限
        if self.admin_var.get():
            cmd.append("--uac-admin")
        
        # 应用名称
        cmd.extend(["--name", self.app_name_var.get()])
        
        # 输出路径
        dist_path = os.path.join(self.output_dir_var.get(), "dist")
        build_path = os.path.join(self.output_dir_var.get(), "build")
        spec_path = self.output_dir_var.get()
        
        cmd.extend(["--distpath", dist_path])
        cmd.extend(["--workpath", build_path])
        cmd.extend(["--specpath", spec_path])
        
        # 图标
        if self.icon_path_var.get() and os.path.exists(self.icon_path_var.get()):
            cmd.extend(["--icon", self.icon_path_var.get()])
        
        # 清理
        if self.clean_var.get():
            cmd.append("--clean")
        
        # 覆盖确认
        if self.no_confirm_var.get():
            cmd.append("--noconfirm")
        
        # ASCII
        if self.ascii_var.get():
            cmd.append("--ascii")
        
        # 调试
        if self.debug_var.get():
            cmd.append("--debug=all")
        
        # 日志级别
        cmd.extend(["--log-level", self.log_level_var.get()])
        
        # Python优化级别
        opt_level = self.optimize_var.get()[0] if self.optimize_var.get() else "0"
        if opt_level != "0":
            cmd.extend(["-O" if opt_level == "1" else "-OO"])
        
        # UPX
        if self.use_upx_var.get():
            if self.upx_dir_var.get():
                cmd.extend(["--upx-dir", self.upx_dir_var.get()])
            else:
                cmd.append("--upx")
        else:
            cmd.append("--noupx")
        
        # 隐藏导入
        for module in self.hidden_imports:
            cmd.extend(["--hidden-import", module])
        
        # 排除模块（不排除样式相关模块）
        for module in self.exclude_modules:
            if module not in ['tkinter', 'ttk', 'customtkinter', 'ctk']:
                cmd.extend(["--exclude-module", module])
        
        # 附加文件
        for item in self.files_listbox.get(0, tk.END):
            if os.path.exists(item):
                if os.path.isfile(item):
                    cmd.extend(["--add-data", f"{item}{os.pathsep}."])
                else:
                    folder_name = os.path.basename(item)
                    cmd.extend(["--add-data", f"{item}{os.pathsep}{folder_name}"])
        
        # 添加Tkinter数据文件以修复Tk data directory问题
        try:
            tcl_lib = tk.Tcl().eval('info library')
            tk_lib = os.path.join(os.path.dirname(tcl_lib), f'tk{tk.TclVersion}')
            tcl_lib = os.path.join(os.path.dirname(tcl_lib), f'tcl{tk.TclVersion}')
            cmd.extend(["--add-data", f"{tcl_lib}{os.pathsep}tcl{tk.TclVersion}"])
            cmd.extend(["--add-data", f"{tk_lib}{os.pathsep}tk{tk.TclVersion}"])
        except Exception as e:
            self._output(f"[WARNING] 无法自动添加Tkinter数据文件: {str(e)}\n")
            self._output("请手动添加附加文件夹: Python安装目录下的tcl/tcl8.6 和 tcl/tk8.6\n")
        
        # 版本文件（如果有版本信息）
        if self._should_create_version_file():
            version_file = self._create_version_file()
            if version_file:
                cmd.extend(["--version-file", version_file])
        
        # 添加Python文件
        cmd.append(self.script_path_var.get())
        
        return cmd
    
    def _should_create_version_file(self):
        """检查是否需要创建版本文件"""
        return (
            sys.platform == "win32" and (
                self.file_version_var.get() != "1.0.0.0" or
                self.product_version_var.get() != "1.0.0" or
                self.company_name_var.get() or
                self.product_name_var.get() or
                self.copyright_var.get() or
                (hasattr(self, 'file_desc_text') and self.file_desc_text.get("1.0", "end-1c"))
            )
        )
    
    def _create_version_file(self):
        """创建版本信息文件"""
        if sys.platform != "win32":
            return None
        
        try:
            version_file = os.path.join(self.output_dir_var.get(), "version_info.txt")
            
            # 解析版本号
            file_ver = self.file_version_var.get() or "1.0.0.0"
            prod_ver = self.product_version_var.get() or "1.0.0"
            
            # 确保版本号格式正确
            file_ver_parts = file_ver.split('.')
            while len(file_ver_parts) < 4:
                file_ver_parts.append('0')
            file_ver_tuple = ', '.join(file_ver_parts[:4])
            
            prod_ver_parts = prod_ver.split('.')
            while len(prod_ver_parts) < 4:
                prod_ver_parts.append('0')
            prod_ver_tuple = ', '.join(prod_ver_parts[:4])
            
            # 获取描述
            file_desc = ""
            if hasattr(self, 'file_desc_text'):
                file_desc = self.file_desc_text.get("1.0", "end-1c").strip()
            
            # 版本信息模板
            version_info = f"""# UTF-8
VSVersionInfo(
  ffi=FixedFileInfo(
    filevers=({file_ver_tuple}),
    prodvers=({prod_ver_tuple}),
    mask=0x3f,
    flags=0x0,
    OS=0x40004,
    fileType=0x1,
    subtype=0x0,
    date=(0, 0)
  ),
  kids=[
    StringFileInfo(
      [
      StringTable(
        u'040904B0',
        [StringStruct(u'CompanyName', u'{self.company_name_var.get()}'),
        StringStruct(u'FileDescription', u'{file_desc}'),
        StringStruct(u'FileVersion', u'{file_ver}'),
        StringStruct(u'InternalName', u'{self.app_name_var.get()}'),
        StringStruct(u'LegalCopyright', u'{self.copyright_var.get()}'),
        StringStruct(u'OriginalFilename', u'{self.app_name_var.get()}.exe'),
        StringStruct(u'ProductName', u'{self.product_name_var.get()}'),
        StringStruct(u'ProductVersion', u'{prod_ver}')])
      ]), 
    VarFileInfo([VarStruct(u'Translation', [1033, 1200])])
  ]
)"""
            
            # 写入文件
            with open(version_file, 'w', encoding='utf-8') as f:
                f.write(version_info)
            
            return version_file
        except Exception as e:
            self._output(f"[WARNING] 创建版本文件失败: {str(e)}\n")
            return None
    
    def _start_packing(self):
        """开始打包"""
        if self.packing:
            messagebox.showwarning("警告", "正在打包中...")
            return
        
        if not self.script_path_var.get():
            messagebox.showwarning("警告", "请选择Python文件")
            return
        
        if not self.output_dir_var.get():
            messagebox.showwarning("警告", "请选择输出目录")
            return
        
        if not self.app_name_var.get():
            messagebox.showwarning("警告", "请输入应用名称")
            return
        
        # 切换到输出标签页
        if USE_CTK:
            self.tabview.set("打包输出")
        else:
            self.tabview.select(4)
        
        self.output_text.delete("1.0", "end")
        self._set_progress(0)
        self.progress_label.configure(text="准备打包...")
        
        self.packing = True
        self.pack_button.configure(state="disabled")
        
        # 启动打包线程
        thread = threading.Thread(target=self._do_pack, daemon=True)
        thread.start()
        
    def _do_pack(self):
        """执行打包"""
        try:
            self._pack_with_pyinstaller()
        except Exception as e:
            self._output(f"\n[ERROR] 打包出错: {str(e)}\n")
            self._output(f"详细错误:\n{traceback.format_exc()}\n")
            self.after(0, lambda: self.progress_label.configure(text="打包失败"))
        finally:
            self.packing = False
            self.after(0, lambda: self.pack_button.configure(state="normal"))
            
    def _pack_with_pyinstaller(self):
        """使用PyInstaller打包"""
        self._output("=== 向日葵Python打包程序 v3.0 ===\n")
        self._output("=== 开始打包 ===\n\n")
        self.after(0, lambda: self.progress_label.configure(text="准备打包环境..."))
        self.after(0, lambda: self._set_progress(0.1))
        
        # 保存当前目录
        original_dir = os.getcwd()
        
        # 切换到Python脚本所在的目录
        script_dir = os.path.dirname(os.path.abspath(self.script_path_var.get()))
        script_name = os.path.basename(self.script_path_var.get())
        
        try:
            # 切换工作目录
            os.chdir(script_dir)
            self._output(f"工作目录: {script_dir}\n")
            
            # 构建命令
            cmd = self._build_command()
            
            # 修改命令中的脚本路径为相对路径
            if cmd[-1] == self.script_path_var.get():
                cmd[-1] = script_name
            
            # 输出命令
            self._output("执行命令:\n")
            self._output(" ".join(cmd) + "\n\n")
            
            self.after(0, lambda: self.progress_label.configure(text="正在打包..."))
            self.after(0, lambda: self._set_progress(0.3))
            
            # 执行打包
            start_time = time.time()
            process = subprocess.Popen(
                cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                universal_newlines=True
            )
            
            # 实时输出
            for line in process.stdout:
                self._output(line)
                
                # 更新进度
                if "Building" in line:
                    self.after(0, lambda: self._set_progress(0.5))
                    self.after(0, lambda: self.progress_label.configure(text="正在构建..."))
                elif "Copying" in line:
                    self.after(0, lambda: self._set_progress(0.7))
                    self.after(0, lambda: self.progress_label.configure(text="正在复制文件..."))
                elif "Building EXE" in line:
                    self.after(0, lambda: self._set_progress(0.9))
                    self.after(0, lambda: self.progress_label.configure(text="正在生成EXE..."))
            
            process.wait()
            elapsed_time = time.time() - start_time
            
            if process.returncode == 0:
                self.after(0, lambda: self._set_progress(1.0))
                self.after(0, lambda: self.progress_label.configure(text="打包成功！"))
                
                self._output(f"\n[OK] 打包成功！耗时: {elapsed_time:.2f}秒\n")
                
                # 输出文件信息
                dist_path = os.path.join(self.output_dir_var.get(), "dist")
                if self.one_file_var.get():
                    output_file = os.path.join(dist_path, f"{self.app_name_var.get()}.exe")
                else:
                    output_file = os.path.join(dist_path, self.app_name_var.get())
                
                if os.path.exists(output_file):
                    if os.path.isfile(output_file):
                        file_size = os.path.getsize(output_file) / (1024 * 1024)
                        self._output(f"输出文件: {output_file}\n")
                        self._output(f"文件大小: {file_size:.2f} MB\n")
                    else:
                        self._output(f"输出目录: {output_file}\n")
                        # 计算目录大小
                        total_size = 0
                        for dirpath, dirnames, filenames in os.walk(output_file):
                            for f in filenames:
                                fp = os.path.join(dirpath, f)
                                total_size += os.path.getsize(fp)
                        total_size_mb = total_size / (1024 * 1024)
                        self._output(f"总大小: {total_size_mb:.2f} MB\n")
                
                # 签名（如果启用）
                if sys.platform == "win32" and self.sign_var.get() and self.cert_path_var.get():
                    self._sign_executable(output_file)
                
                # 打开输出目录
                if os.path.exists(dist_path):
                    if sys.platform == "win32":
                        os.startfile(dist_path)
                    elif sys.platform == "darwin":
                        subprocess.run(["open", dist_path])
                    else:
                        subprocess.run(["xdg-open", dist_path])
                    
                    messagebox.showinfo(
                        "打包成功", 
                        f"打包完成！\n\n输出目录: {dist_path}\n耗时: {elapsed_time:.2f}秒"
                    )
            else:
                self.after(0, lambda: self.progress_label.configure(text="打包失败"))
                self._output(f"\n[ERROR] 打包失败！返回代码: {process.returncode}\n")
                
                self._output("\n可能的解决方案:\n")
                self._output("1. 确保Python脚本能正常运行\n")
                self._output("2. 检查是否安装了所有依赖包\n")
                self._output("3. 尝试关闭杀毒软件（可能误报）\n")
                self._output("4. 确保路径中没有中文或特殊字符\n")
                self._output("5. 尝试先不勾选'窗口模式'查看错误信息\n")
                self._output("6. 检查是否有足够的磁盘空间\n")
                self._output("7. 尝试使用管理员权限运行\n")
        finally:
            # 恢复原始工作目录
            os.chdir(original_dir)
    
    def _sign_executable(self, exe_path):
        """签名可执行文件"""
        if not os.path.exists(self.cert_path_var.get()):
            self._output("[WARNING] 证书文件不存在，跳过签名\n")
            return
        
        self._output("\n正在签名...\n")
        
        # 这里需要signtool.exe，通常在Windows SDK中
        # 简化示例，实际使用需要更多参数
        try:
            cmd = [
                "signtool", "sign",
                "/f", self.cert_path_var.get(),
                "/t", "http://timestamp.digicert.com",
                exe_path
            ]
            
            result = subprocess.run(cmd, capture_output=True, text=True)
            if result.returncode == 0:
                self._output("[OK] 签名成功\n")
            else:
                self._output(f"[WARNING] 签名失败: {result.stderr}\n")
        except FileNotFoundError:
            self._output("[WARNING] signtool未找到，请安装Windows SDK\n")
        except Exception as e:
            self._output(f"[WARNING] 签名出错: {str(e)}\n")
            
    def _set_progress(self, value):
        """设置进度条"""
        if USE_CTK:
            self.progress_bar.set(value)
        else:
            self.progress_bar['value'] = value * 100
            
    def _output(self, text):
        """输出文本"""
        self.after(0, lambda: self._append_output(text))
        
    def _append_output(self, text):
        """追加输出"""
        self.output_text.insert("end", text)
        if USE_CTK:
            self.output_text.see("end")
        else:
            self.output_text.see(tk.END)
        self.update()

def main():
    """主函数"""
    # 检查Python版本
    if sys.version_info < (3, 6):
        print("错误: 需要Python 3.6或更高版本")
        sys.exit(1)
    
    # 检查基本依赖
    try:
        import tkinter
    except ImportError:
        print("错误: tkinter未安装")
        sys.exit(1)
    
    # 检查PyInstaller
    try:
        subprocess.run(["pyinstaller", "--version"], capture_output=True, check=True, timeout=5)
    except:
        print("PyInstaller未安装，请运行: pip install pyinstaller")
        response = input("是否自动安装? (y/n): ")
        if response.lower() == 'y':
            subprocess.run([sys.executable, "-m", "pip", "install", "pyinstaller"])
        else:
            print("\n请手动安装后再运行本程序")
            sys.exit(1)
    
    # 如果本身以窗口模式运行，不显示控制台
    if sys.platform == "win32":
        try:
            # 尝试隐藏控制台窗口
            import ctypes
            kernel32 = ctypes.WinDLL('kernel32')
            user32 = ctypes.WinDLL('user32')
            
            # 获取控制台窗口句柄
            hWnd = kernel32.GetConsoleWindow()
            if hWnd:
                # 隐藏窗口
                user32.ShowWindow(hWnd, 0)
        except:
            pass
    
    # 启动应用
    app = PyPackerGUI()
    app.mainloop()

if __name__ == "__main__":
    main()